package com.wh.hotel.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.wh.common.util.DistanceUtil;
import com.wh.hotel.cache.HotelInfoCache;
import com.wh.hotel.convert.HotelStructConvert;
import com.wh.hotel.dao.HotelBaseInfoDao;
import com.wh.hotel.enums.HotelSortTypeEnum;
import com.wh.hotel.enums.HotelStatusEnum;
import com.wh.hotel.enums.base.HotelOperateTypeEnum;
import com.wh.hotel.model.HotelBaseInfo;
import com.wh.hotel.param.HotelSearchParam;
import com.wh.hotel.service.HotelBaseService;
import com.wh.hotel.util.PageResultUtil;
import com.wh.hotel.vo.HotelBaseInfoVo;
import com.wh.hotel.vo.HotelListWebVo;
import com.wh.hotel.vo.HotelQueryVo;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author wenhao
 */
@Service
public class HotelBaseServiceImpl implements HotelBaseService {

    @Resource
    private HotelBaseInfoDao hotelBaseInfoDao;

    @Resource
    private HotelStructConvert hotelStructConvert;

    @Resource
    private HotelInfoCache hotelInfoCache;

    @Override
    public HotelBaseInfo addHotel(HotelBaseInfoVo param) {
        HotelBaseInfo hotelBaseInfo = hotelStructConvert.convertToHotelBaseInfo(param);
        // 新增酒店默认是下架的
        hotelBaseInfo.setHotelStatus(HotelStatusEnum.OFFLINE.getCode());
        if (!hotelBaseInfoDao.insert(hotelBaseInfo)) {
            return null;
        }
        // todo 发mq广播
        return hotelBaseInfo;
    }

    @Override
    public IPage<HotelBaseInfoVo> list(HotelQueryVo hotelQueryVo) {
        IPage<HotelBaseInfo> hotelBaseInfoPage = hotelBaseInfoDao.queryPageByCondition(hotelQueryVo);
        IPage<HotelBaseInfoVo> hotelBaseInfoVoPage = new Page<>();
        PageResultUtil.copyPageInfo(hotelBaseInfoPage, hotelBaseInfoVoPage);
        List<HotelBaseInfoVo> hotelBaseInfoVoList = hotelBaseInfoPage.getRecords().stream()
                .map(hotelBaseInfo -> hotelStructConvert.convertToHotelBaseInfoVo(hotelBaseInfo))
                .collect(Collectors.toList());
        hotelBaseInfoVoPage.setRecords(hotelBaseInfoVoList);
        return hotelBaseInfoVoPage;
    }

    @Override
    public boolean operateStatus(HotelBaseInfoVo param) {
        HotelOperateTypeEnum operateTypeEnum = HotelOperateTypeEnum.parse(param.getOperateType());
        if (operateTypeEnum == null) {
            throw new RuntimeException("操作类型不合法");
        }
        // todo 发mq广播
        return hotelBaseInfoDao.operateHotelStatus(param.getId(), operateTypeEnum);
    }

    @Override
    public boolean editHotel(HotelBaseInfoVo param) {
        HotelBaseInfo hotelBaseInfo = hotelStructConvert.convertToHotelBaseInfo(param);
        hotelBaseInfo.setId(param.getId());
        // todo 发mq广播
        return hotelBaseInfoDao.updateById(hotelBaseInfo);
    }

    @Override
    public IPage<HotelListWebVo> hotelSearch(HotelSearchParam param) {
        // 过滤数据&并且转换
        List<HotelListWebVo> searchHotelList = filterHotelInfo(param);
        // 填充距离数据
        if (param.getLongitude() != null && param.getLatitude() != null) {
            searchHotelList.forEach(vo -> {
                double distance = DistanceUtil.calculateGeoForKMeter(vo.getLatitude(), vo.getLongitude(),
                        param.getLatitude(), param.getLongitude());
                BigDecimal distanceKm = new BigDecimal(distance).setScale(2, RoundingMode.HALF_DOWN);
                vo.setDistance(distanceKm);
            });
            if (param.getSortType() == HotelSortTypeEnum.DISTANCE.getCode()) {
                searchHotelList.sort(Comparator.comparing(HotelListWebVo::getDistance));
            }
        }
        return PageResultUtil.page(searchHotelList, param.getPageNum(), param.getPageSize());
    }

    @Override
    public HotelBaseInfoVo detail(HotelQueryVo hotelQueryVo) {
        List<HotelBaseInfo> hotelBaseInfoList = hotelBaseInfoDao.selectBatchById(Lists.newArrayList(hotelQueryVo.getId()));
        if (CollectionUtils.isEmpty(hotelBaseInfoList)) {
            return null;
        }
        return hotelStructConvert.convertToHotelBaseInfoVo(hotelBaseInfoList.get(0));
    }

    private List<HotelListWebVo> filterHotelInfo(HotelSearchParam param) {
        List<HotelBaseInfo> hotelInfoCacheAll = hotelInfoCache.getNormalAll();
        return hotelInfoCacheAll.stream()
                .filter(hotelBaseInfo -> {
                    // 城市
                    if (StringUtils.isNotBlank(param.getCity()) && !StringUtils.equals(param.getCity(), hotelBaseInfo.getCity())) {
                        return false;
                    }
                    // 搜索词
                    return filterBySearchWord(hotelBaseInfo, param.getSearchWord());
                })
                .map(hotelBaseInfo -> hotelStructConvert.convertToHotelListWebVo(hotelBaseInfo))
                .collect(Collectors.toList());
    }

    private boolean filterBySearchWord(HotelBaseInfo hotelBaseInfo, String searchWord) {
        if (StringUtils.isBlank(searchWord)) {
            return true;
        }
        double hotelNameSimilarity = DistanceUtil.calculateSimilarityFromEdit(hotelBaseInfo.getHotelName(), searchWord);
        if (hotelNameSimilarity > DistanceUtil.EPS) {
            return true;
        }
        double hotelAddressSimilarity = DistanceUtil.calculateSimilarityFromEdit(hotelBaseInfo.getHotelAddress(), searchWord);
        if (hotelAddressSimilarity > DistanceUtil.EPS) {
            return true;
        }
        return false;
    }
}
